/**
 * DelayQueue类
 *
 * @author fireway
 * @since 2019年 03月 06日 星期三 06:53:49 CST
 */
package concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class DelayedTask implements Runnable, Delayed {
    private static int sCounter = 0;

    private final int mId = sCounter++;

    private final int mDelta;

    private final long mTrigger;

    protected static List<DelayedTask> sSequence = new ArrayList<DelayedTask>();

    public DelayedTask(int delayInMilliseconds) {
        mDelta = delayInMilliseconds;
        mTrigger = System.nanoTime() + TimeUnit.NANOSECONDS.convert(mDelta, TimeUnit.MILLISECONDS);
        sSequence.add(this);
    }

    public String summary() {
        return "(Task " + mId + ":" + mDelta + ")";
    }

    @Override
    public String toString() {
        return String.format("[%1$-4d]", mDelta) + " Task " + mId;
    }

    @Override
    public void run() {
        System.out.println(this);
    }

    @Override
    public int compareTo(Delayed arg) {
        DelayedTask that = (DelayedTask) arg;

        if (mTrigger < that.mTrigger) {
            return -1;
        } else if (mTrigger > that.mTrigger) {
            return 1;
        } else {
            return 0;
        }
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(mTrigger - System.nanoTime(), TimeUnit.NANOSECONDS);
    }

    public static class EndSentinel extends DelayedTask {
        private ExecutorService mExec;

        public EndSentinel(int delay, ExecutorService e) {
            super(delay);
            mExec = e;
        }

        @Override
        public void run() {
            int count = 0;
            for(DelayedTask dt : sSequence) {
                System.out.print(dt.summary() + ", ");
                if(++count % 5 == 0) {
                    System.out.println();
                }
            }
            System.out.println("");
            System.out.println(this + " Calling shutdownNow()");
            mExec.shutdownNow();
        }
    }
}

class DelayedTaskConsumer implements Runnable {
    private DelayQueue<DelayedTask> mDelayQueue;

    public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
        mDelayQueue = q;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                // Run task with the current thread
                mDelayQueue.take().run();
            }
        } catch (InterruptedException e) {
            // Acceptable way to exit
        }
        System.out.println("Finished DelayedTaskConsumer");
    }
}

public class DelayQueueDemo {
    public static void main(String[] args) {
        Random rand = new Random(47);
        DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();
        // Fill with tasks that have random delays:
        for(int i = 0; i < 20; i++) {
            queue.put(new DelayedTask(rand.nextInt(5000)));
        }

        ExecutorService exec = Executors.newCachedThreadPool();
        // Set the stopping point
        queue.add(new DelayedTask.EndSentinel(5000, exec));
        exec.execute(new DelayedTaskConsumer(queue));
    }
}
